from PySide6.QtWidgets import QSplitter, QLayout, QHBoxLayout, QWidget, QTabWidget, QTextEdit
from PySide6.QtWidgets import QTableWidget, QTableWidgetItem, QProgressBar, QPushButton
from PySide6.QtCore import Qt
from collections import OrderedDict
from typing import Optional, List, Dict

import openpyxl
import xlrd  # type: ignore


class ProgressDisplayer(QWidget):

    def __init__(self) -> None:
        super().__init__(None)
        self.progress_bar = QProgressBar(self)
        self.stop_btn = QPushButton("停止", self)

        layout = QHBoxLayout()
        layout.addWidget(self.progress_bar, 5)
        layout.addWidget(self.stop_btn, 1)

        self.setLayout(layout)
        self.progress_bar.setFormat("任务执行中 (%v/%m, %p%)")
        self.progress_bar.setStyleSheet("background: transparent;")

    def setProgress(self, value: int, maximum: int) -> None:
        if value == maximum:
            self.hide()
            return

        self.show()
        self.progress_bar.setMaximum(maximum)
        self.progress_bar.setValue(value)


class ExtendedSplitter(QSplitter):

    def __init__(self, direction: Qt.Orientation) -> None:
        super().__init__(direction)

    def addLayout(self, layout: QLayout) -> None:
        widget = QWidget()
        widget.setLayout(layout)
        self.addWidget(widget)

    def setStretchFactors(self, indexes: List[int]) -> None:
        for i, index in enumerate(indexes):
            self.setStretchFactor(i, index)


class ExtendedTableWidget(QTableWidget):

    def __init__(self, name: str, parent: Optional[QWidget] = None) -> None:
        super().__init__(parent)
        self.name = name

    def getColumnHeaderLabel(self, i: int) -> str:
        header_item = self.horizontalHeaderItem(i)
        if header_item is not None:
            return header_item.text()
        else:
            return ""

    def getColumnHeaderLabels(self) -> List[str]:
        return [self.getColumnHeaderLabel(i) for i in range(self.columnCount())]

    def hasColumnHeader(self, header: str) -> bool:
        return header in self.getColumnHeaderLabels()

    def getColumnIndex(self, header: str) -> int:
        return self.getColumnHeaderLabels().index(header)

    def getRowContent(self, row: int) -> OrderedDict[str, str]:
        ret = OrderedDict()
        for i in range(self.columnCount()):
            item = self.item(row, i)
            ret[self.getColumnHeaderLabel(i)] = '' if item is None else item.text()

        return ret

    def createColumnHeader(self, header: str) -> None:
        if not self.hasColumnHeader(header):
            self.setColumnCount(self.columnCount() + 1)
            self.setHorizontalHeaderItem(self.columnCount() - 1, QTableWidgetItem(header))

    def setCellText(self, row: int, col: int, text: str) -> None:
        item = QTableWidgetItem(text)
        self.setItem(row, col, item)


class TableContainer(ExtendedSplitter):

    def __init__(self) -> None:
        super().__init__(Qt.Orientation.Vertical)
        self.setContentsMargins(5, 10, 5, 10)
        self.widget_preview = QTextEdit()
        self.widget_tabs = QTabWidget()
        self.widget_tabs.setTabPosition(QTabWidget.TabPosition.South)
        self.widget_tabs.currentChanged.connect(self.updatePreview)

        self.widget_preview.setReadOnly(True)

        self.addWidget(self.widget_preview)
        self.addWidget(self.widget_tabs)
        self.setSizes([100, 400])

        self.createEmptyTable()

    def createEmptyTable(self) -> None:
        self.createTable("工作表1")

    def createTable(self, name: str) -> ExtendedTableWidget:
        table = ExtendedTableWidget(name)
        self.widget_tabs.addTab(table, name)
        return table

    def openExcelXlsxFile(self, excel_path: str) -> None:
        self.widget_tabs.clear()
        workbook = openpyxl.load_workbook(excel_path)

        for sheet in workbook.sheetnames:
            table_widget = self.createTable(sheet)
            worksheet = workbook[sheet]
            table_widget.setRowCount(worksheet.max_row)
            table_widget.setColumnCount(worksheet.max_column)
            table_widget.itemSelectionChanged.connect(self.updatePreview)

            for row_idx, row in enumerate(worksheet.iter_rows(values_only=True)):
                if row_idx == 0:
                    table_widget.setHorizontalHeaderLabels([str(cell) for cell in row])
                    continue

                for col_idx, cell_value in enumerate(row):
                    if cell_value is None:
                        cell_str = ""
                    else:
                        cell_str = str(cell_value)

                    item = QTableWidgetItem(cell_str)
                    table_widget.setItem(row_idx - 1, col_idx, item)

    def openExcelXlsFile(self, excel_path: str) -> None:
        self.widget_tabs.clear()
        workbook = xlrd.open_workbook(excel_path)

        for sheet in workbook.sheet_names():
            table_widget = self.createTable(sheet)
            worksheet = workbook.sheet_by_name(sheet)
            table_widget.setRowCount(worksheet.nrows)
            table_widget.setColumnCount(worksheet.ncols)
            table_widget.itemSelectionChanged.connect(self.updatePreview)

            for row_idx in range(worksheet.nrows):
                row = worksheet.row_values(row_idx)
                if row_idx == 0:
                    table_widget.setHorizontalHeaderLabels([str(cell) for cell in row])
                    continue

                for col_idx, cell_value in enumerate(row):
                    if cell_value is None:
                        cell_str = ""
                    else:
                        cell_str = str(cell_value)

                    item = QTableWidgetItem(cell_str)
                    table_widget.setItem(row_idx - 1, col_idx, item)

    def saveExcelFile(self, excel_path: str) -> None:
        workbook = openpyxl.Workbook()
        if workbook.active:
            workbook.remove(workbook.active)  # Remove the default sheet created

        for index in range(self.widget_tabs.count()):
            table_widget = self.widget_tabs.widget(index)
            assert isinstance(table_widget, ExtendedTableWidget)

            sheet_name = self.widget_tabs.tabText(index)
            worksheet = workbook.create_sheet(title=sheet_name)
            for i, lbl in enumerate(table_widget.getColumnHeaderLabels()):
                worksheet.cell(row=1, column=i + 1, value=lbl)

            for row in range(table_widget.rowCount()):
                for col in range(table_widget.columnCount()):
                    item = table_widget.item(row, col)
                    if item is not None:
                        worksheet.cell(row=row + 2, column=col + 1, value=item.text())

        workbook.save(excel_path)

    def activeTable(self) -> ExtendedTableWidget:
        widget = self.widget_tabs.currentWidget()
        assert isinstance(widget, ExtendedTableWidget)
        return widget

    def getTableByName(self, name: str) -> ExtendedTableWidget:
        for i in range(self.widget_tabs.count()):
            widget = self.widget_tabs.widget(i)
            assert isinstance(widget, ExtendedTableWidget)
            if widget.name == name:
                return widget

        raise KeyError(f"table {name} not found")

    def updatePreview(self) -> None:
        table_widget = self.widget_tabs.currentWidget()
        if not isinstance(table_widget, QTableWidget):
            return

        selected_items = table_widget.selectedItems()
        if len(selected_items) > 0:
            self.widget_preview.setPlainText(selected_items[0].text())
